package com.tj.collection.utils;

import com.fqgj.common.utils.TimeUtils;

import java.math.BigDecimal;
import java.util.Date;

/**
 * 交易各种金额统一计算器
 * * 砍头息=合同金额-到账金额
 * 针对Cashmama这种产品 目前其实是把 砍头息当成了服务费来处理的这样
 * 在处理多期产品的时候就会有问题，会导致每期都会收一笔砍头息。。。。
 * so 多期需要改造。
 *
 * @author zsp
 */
public class CalculatorUtils {
    /**
     * 保留位数
     */
    private static final int SCALE = 0;
    private static final int SCALE_TWO = 2;
    /**
     * 取舍方式
     */
    private static final int ROUNDING_MODE = BigDecimal.ROUND_HALF_UP;


    /**
     * 计算每期借款金额
     * 每期借款金额=借款合同金额/期数
     *
     * @param borrowTotalCapital 借款合同金额
     * @param totalPeriods       期数
     */
    public static BigDecimal calculateBorrowCapital(BigDecimal borrowTotalCapital, int totalPeriods) {
        return borrowTotalCapital.divide(new BigDecimal(totalPeriods), SCALE, ROUNDING_MODE);
    }

    /**
     * 计算最后一期借款金额 （因为除法会有金额损失）
     * 最后后一期需要用总-每期*(总期数-1)) || 总- 总/每期+每期
     *
     * @param borrowTotalCapital 借款总金额
     * @param totalPeriods       总期数
     */
    public static BigDecimal calculateLastBorrowCapital(BigDecimal borrowTotalCapital, int totalPeriods) {
        BigDecimal borrowCapital = calculateBorrowCapital(borrowTotalCapital, totalPeriods);
        return borrowTotalCapital.subtract(borrowCapital.multiply(new BigDecimal(totalPeriods - 1)));
    }

    /**
     * 计算每期到账金额
     * 每期到账金额=到账总金额/期数
     *
     * @param totalReceivedCapital 到账总金额
     * @param totalPeriods         期数
     */
    public static BigDecimal calculateReceivedCapital(BigDecimal totalReceivedCapital, int totalPeriods) {
        return totalReceivedCapital.divide(new BigDecimal(totalPeriods), SCALE, ROUNDING_MODE);
    }

    /**
     * 计算最后一期到账金额
     * 最后一期需要用总-每期*(总期数-1)) || 总- 总/每期+每期
     *
     * @param totalReceivedCapital 到账总金额
     * @param totalPeriods         总期数
     */
    public static BigDecimal calculateLastReceivedCapital(BigDecimal totalReceivedCapital, int totalPeriods) {
        BigDecimal borrowCapital = calculateReceivedCapital(totalReceivedCapital, totalPeriods);
        return totalReceivedCapital.subtract(borrowCapital.multiply(new BigDecimal(totalPeriods - 1)));
    }

    /**
     * 计算每期服务费
     * 每期服务费=总服务费/期数  (最后一期需要用总-每期*(总期数-1))
     *
     * @param totalServiceFee 总服务费
     * @param totalPeriods    期数
     */
    public static BigDecimal calculateServiceFee(BigDecimal totalServiceFee, int totalPeriods) {
        return totalServiceFee.divide(new BigDecimal(totalPeriods), SCALE, ROUNDING_MODE);
    }

    /**
     * 计算最后一期服务费
     * 最后一期需要用总-每期*(总期数-1)) || 总- 总/每期+每期
     *
     * @param totalServiceFee 总服务费
     * @param totalPeriods    总期数
     */
    public static BigDecimal calculateLastServiceFee(BigDecimal totalServiceFee, int totalPeriods) {
        BigDecimal borrowCapital = calculateServiceFee(totalServiceFee, totalPeriods);
        return totalServiceFee.subtract(borrowCapital.multiply(new BigDecimal(totalPeriods - 1)));
    }


    /**
     * 计算日利率
     *
     * @param yearRatio 年利率
     * @return 日利率
     */
    public static BigDecimal calculateDayRatio(BigDecimal yearRatio) {
        return yearRatio.divide(BigDecimal.valueOf(365), 5, ROUNDING_MODE);
    }

    /**
     * 计算利息(不保留小数)
     * 利息=金额*年利率/365*天数
     *
     * @param borrowCapital 金额
     * @param borrowRatio   年利率
     * @param periodLength  天数
     * @return 利息
     */
    public static BigDecimal calculateInterestFee(BigDecimal borrowCapital, BigDecimal borrowRatio, int periodLength) {
        return borrowCapital.multiply(borrowRatio)
                .multiply(BigDecimal.valueOf(periodLength))
                .divide(BigDecimal.valueOf(365), SCALE, ROUNDING_MODE);
    }


    /**
     * 时应还总金额
     * <p>
     * 理论公式  应还总金额=借款合同金额+总利息+总服务费
     */
    public static BigDecimal createOrderRepaymentTotalCapital(BigDecimal borrowTotalCapital,
                                                              BigDecimal totalInterestFee) {
        return borrowTotalCapital.add(totalInterestFee);
    }

    /**
     * 还款时应还总金额
     * 理论公式
     * 应还总金额=借款合同金额+总利息+总服务费+总罚息+罚息服务费
     *
     * @param borrowTotalCapital 借款金额
     * @param totalInterestFee   总利息
     * @param totalOverdueFee    总罚息
     * @param overdueServiceFee  罚息服务费
     */
    public static BigDecimal repaymentTotalCapital(BigDecimal borrowTotalCapital,
                                                   BigDecimal totalInterestFee,
                                                   BigDecimal totalOverdueFee,
                                                   BigDecimal overdueServiceFee) {
        return borrowTotalCapital.add(totalInterestFee).add(totalOverdueFee).add(overdueServiceFee);
    }

    /**
     * 还款时应还总金额
     * 理论公式
     * 应还总金额=借款合同金额+总利息+总服务费+总罚息+罚息服务费
     *
     * @param repaymentCapital  生成账单时本期应还金额
     * @param totalOverdueFee   总罚息
     * @param overdueServiceFee 罚息服务费
     */
    public static BigDecimal repaymentTotalCapital(BigDecimal repaymentCapital,
                                                   BigDecimal totalOverdueFee,
                                                   BigDecimal overdueServiceFee) {
        return repaymentCapital.add(totalOverdueFee).add(overdueServiceFee);
    }

    /**
     * 生成账单时每期应还金额
     * 理论公式
     * 1 应还金额=本期合同金额+本期利息+本期服务费
     */
    public static BigDecimal createBillRepaymentCapital(BigDecimal borrowCapital, BigDecimal interestFee) {
        return borrowCapital.add(interestFee);
    }

    /**
     * 计算还款时每期应还金额
     * 理论公式
     * 1 应还金额=生成账单时每期应还金额+本期罚息+本期罚息服务费-已还金额(已还金额已经包含抵扣)
     */
    public static BigDecimal calculateRepaymentCapital(BigDecimal createBillRepaymentCapital,
                                                       BigDecimal overdueFee,
                                                       BigDecimal overdueServiceFee,
                                                       BigDecimal paidCapital
    ) {
        return createBillRepaymentCapital.add(overdueFee).add(overdueServiceFee).subtract(paidCapital);
    }

    /**
     * 是否逾期
     *
     * @param repaymentDate 应还时间
     * @param payOffDate    还清时间
     */
    public static boolean isOverdue(Date repaymentDate, Date payOffDate) {
        repaymentDate = TimeUtils.getDate("yyyy-MM-dd", repaymentDate);
        Date nowDate = TimeUtils.getDate("yyyy-MM-dd", payOffDate != null ? payOffDate : new Date());
        return repaymentDate.getTime() < nowDate.getTime();
    }

    /**
     * 逾期天数
     *
     * @param repaymentDate 应还时间
     * @param payOffDate    还清时间
     */
    public static int overDueDays(Date repaymentDate, Date payOffDate) {
        repaymentDate = TimeUtils.getDate("yyyy-MM-dd", repaymentDate);
        Date nowDate = TimeUtils.getDate("yyyy-MM-dd", payOffDate != null ? payOffDate : new Date());
        Integer overdueDays = TimeUtils.diffDays(repaymentDate, nowDate);
        if (overdueDays < 0) {
            overdueDays = 0;
        }
        return overdueDays;
    }

    /**
     * 计算逾期费(每笔)
     * 逾期费=每笔账单借款合同金额*日利率*逾期天数
     *
     * @param borrowCapital 每笔账单借款合同金额
     * @param penaltyRate   日利率
     * @param overDueDays   逾期天数
     */
    public static BigDecimal calculateOverdueFee(BigDecimal borrowCapital, BigDecimal penaltyRate, int overDueDays) {
        //逾期费
        BigDecimal overdueFee = borrowCapital.multiply(penaltyRate).multiply(new BigDecimal(overDueDays)).setScale(SCALE, ROUNDING_MODE);
        //封顶金额
        BigDecimal capAmount = borrowCapital.multiply(BigDecimal.valueOf(0.9)).setScale(SCALE, ROUNDING_MODE);
        return overdueFee.compareTo(capAmount) > 0 ? capAmount : overdueFee;
    }

    public static BigDecimal calculateOverdueFeeTwoPlace(BigDecimal borrowCapital, BigDecimal penaltyRate, int overDueDays) {
        //逾期费
        BigDecimal overdueFee = borrowCapital.multiply(penaltyRate).multiply(new BigDecimal(overDueDays)).setScale(SCALE_TWO, ROUNDING_MODE);
        //封顶金额
        BigDecimal capAmount = borrowCapital.multiply(BigDecimal.valueOf(1)).setScale(SCALE_TWO, ROUNDING_MODE);
        return overdueFee.compareTo(capAmount) > 0 ? capAmount : overdueFee;
    }

    public static void main(String[] args) {
        System.out.println(calculateOverdueFeeTwoPlace(new BigDecimal(2684), new BigDecimal(0.01), 1));
    }


    /**
     * 是否还清
     * 生成账单时本期应还金额+本期罚息+本期罚息服务费 <= 已还金额+当前还款金额+当前抵扣金额
     *
     * @param repaymentCapital   生成账单时本期应还金额
     * @param overdueFee         本期罚息
     * @param overdueServiceFee  本期罚息服务费
     * @param paidCapital        已还款金额
     * @param capital            当前还款金额
     * @param nowDiscountCapital 当前抵扣金额
     * @return
     */
    public static boolean payClear(BigDecimal repaymentCapital, BigDecimal overdueFee, BigDecimal overdueServiceFee,
                                   BigDecimal paidCapital, BigDecimal capital, BigDecimal nowDiscountCapital) {
        //应还<=实际还款 可当做结清
        return repaymentCapital.add(overdueFee).add(overdueServiceFee).compareTo(
                paidCapital.add(capital).add(nowDiscountCapital)) <= 0;
    }

}
